package taskservice

import (
	"errors"
	"fmt"
	"strings"
	"yunj/pkg/global"
	"yunj/pkg/yunj/app/dto"
	"yunj/pkg/yunj/app/enum/admin/taskenum"
	"yunj/pkg/yunj/app/model"
	"yunj/pkg/yunj/app/service/admin/adminservice"
	"yunj/pkg/yunj/app/validator/admin/taskvalidator"
	"yunj/pkg/yunj/core/builder"
	"yunj/pkg/yunj/core/builder/builderconsts"
	"yunj/pkg/yunj/core/builder/builderdto"
	"yunj/pkg/yunj/core/builder/form"
	"yunj/pkg/yunj/core/builder/form/field"
	"yunj/pkg/yunj/core/builder/table"
	"yunj/pkg/yunj/core/builder/table/col"
	"yunj/pkg/yunj/core/builder/table/col/action"
	"yunj/pkg/yunj/core/builder/table/deftoolbar"
	"yunj/pkg/yunj/core/builder/table/toolbar"
	"yunj/pkg/yunj/core/consts"
	"yunj/pkg/yunj/util"

	"github.com/gin-gonic/gin"
	"github.com/worklz/go-validate"
	"gorm.io/gorm"
)

type taskPage struct {
}

var TaskPage *taskPage

// 列表构建器
func (s *taskPage) ListBuilder(ctx *gin.Context) *builder.Table {
	return builder.NewTable(ctx, "TaskList").
		State(builder.GetEnumTableState(taskenum.StatusEnum, true)).
		Filter(func(t *builder.Table, state *builderdto.TableState) (fields []form.FieldInterface, err error) {
			taskIds := util.QueryParam(ctx, "task_ids", "")
			fields = []form.FieldInterface{
				field.NewDropdownSearch("member_id", "管理员").SetOptionsApi(util.BuildAdminUrl("/member/dropdownSearchOptions")).SetAuth("yunj_task_list_filter_member"),
				field.NewSelect("type", "类型").SetOptions(builder.GetEnumSelectOptions(taskenum.TypeEnum, true)).SetAuth("yunj_task_list_filter_normal"),
				field.NewText("task_ids", "任务ID").SetPlaceholder("完全匹配。批量使用“,”逗号分隔").SetDefault(taskIds),
				field.NewText("title", "标题").SetPlaceholder("模糊匹配").SetAuth("yunj_task_list_filter_normal"),
			}
			return
		}).
		Toolbar(func(t *builder.Table, state *builderdto.TableState) (actions []toolbar.ActionInterface, err error) {
			actions = []toolbar.ActionInterface{}
			if state.Code == taskenum.STATUS_PENDING {
				actions = append(actions,
					toolbar.NewAsyncEvent(taskenum.STATUS_CANCELLED, "取消").SetAuth("yunj_task_list_cancelled"),
					toolbar.NewAsyncEvent("immediate_handle", "立即执行").SetAuth("yunj_task_list_immediate_handle"),
				)
			} else if state.Code == taskenum.STATUS_PROCESSING {
				actions = append(actions,
					toolbar.NewAsyncEvent(taskenum.STATUS_CANCELLED, "取消").SetAuth("yunj_task_list_cancelled"),
				)
			} else if state.Code == taskenum.STATUS_FAILED || state.Code == taskenum.STATUS_CANCELLED {
				actions = append(actions,
					toolbar.NewAsyncEvent("re_handle", "重新执行").SetAuth("yunj_task_list_re_handle"),
				)
			}
			return
		}).
		DefaultToolbar([]deftoolbar.ActionInterface{
			deftoolbar.NewFilter(),
		}).
		Cols(func(t *builder.Table, state *builderdto.TableState) (cols []table.ColInterface, err error) {
			cols = []table.ColInterface{
				col.NewCheckbox("id"),
				col.NewText("created_at", "创建时间").SetMinWidth(150),
				col.NewText("task_id", "任务ID"),
				col.NewText("member_name", "管理员"),
				col.NewEnum("type", "类型").SetMinWidth(110).SetOptions(builder.GetEnumTableColOptions(taskenum.TypeEnum)),
				col.NewText("title", "标题"),
				col.NewText("rate_desc", "进度"),
				col.NewText("latency_desc", "耗时"),
				col.NewCustom("time_line_desc", "执行过程").SetHtml(`<div class="item-time-line-desc">{{- d.time_line_desc }}</div>`).SetMinWidth(380),
				col.NewEnum("status", "状态").SetOptions(builder.GetEnumTableColOptions(taskenum.StatusEnum)).SetAlign(builderconsts.ALIGN_CENTER).SetMaxWidth(80),
			}
			// 操作
			cols = append(cols, col.NewAction("操作").SetOptions([]action.ActionInterface{
				action.NewAsyncEvent(taskenum.STATUS_CANCELLED, "取消").SetAuth("yunj_task_list_cancelled").
					SetShow(fmt.Sprintf("d.status===%d || d.status===%d", taskenum.STATUS_PENDING, taskenum.STATUS_PROCESSING)).
					SetConfirmFormFields(func() (fields []form.FieldInterface, err error) {
						fields = []form.FieldInterface{
							field.NewTextarea("reason", "取消原因").SetVerify("required"),
						}
						return
					}),
				action.NewAsyncEvent("immediate_handle", "立即执行").SetShow(fmt.Sprintf("d.status===%d", taskenum.STATUS_PENDING)).SetAuth("yunj_task_list_immediate_handle"),
				action.NewAsyncEvent("re_handle", "重新执行").SetShow(fmt.Sprintf("d.status===%d || d.status===%d", taskenum.STATUS_FAILED, taskenum.STATUS_CANCELLED)).SetAuth("yunj_task_list_re_handle"),
				action.NewTrigger("export_file_download", "立即下载").SetShow(fmt.Sprintf("d.type===%d && d.status===%d", taskenum.TYPE_TABLE_BUILDER_EXPORT, taskenum.STATUS_COMPLETED)).SetAuth("yunj_task_list_export_file_download"),
			}))
			return
		}).
		Validator(func(t *builder.Table) (validate.ValidatorInterface, error) {
			v := &taskvalidator.Task{}
			v.InitValidator(v)
			return v, nil
		}).
		Count(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableCountResult, err error) {
			query := global.MySQL.Model(&model.AdminTask{})
			var filterFormParam dto.TaskPageListFilterFormParam
			util.MapToStruct(requestParam.FilterData(), &filterFormParam)
			if err = s.listBuilderFilterQuery(ctx, query, requestParam, &filterFormParam); err != nil {
				return
			}
			err = query.Count(&res.Count).Error
			if err != nil {
				err = fmt.Errorf("数据量查询异常！%v", err)
				return
			}
			return
		}).
		Items(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableItemsResult, err error) {
			query := global.MySQL.Preload("Member").Select("id", "member_id", "title", "type", "snap", "status", "rate", "latency", "created_at").Order("created_at desc")
			var filterFormParam dto.TaskPageListFilterFormParam
			util.MapToStruct(requestParam.FilterData(), &filterFormParam)
			if err = s.listBuilderFilterQuery(ctx, query, requestParam, &filterFormParam); err != nil {
				return
			}
			var taskItems []*model.AdminTask
			if err = query.Limit(requestParam.Limit()).Offset(requestParam.Offset()).Find(&taskItems).Error; err != nil {
				err = fmt.Errorf("数据查询异常！%v", err)
				return
			}
			res.Items, err = s.listBuilderItemsHandle(taskItems, t)
			return
		}).
		Event(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableEventResult, err error) {
			// 在验证器中已判断权限
			if requestParam.EventCodeEqual(taskenum.STATUS_CANCELLED) {
				// 取消执行（取消执行原因）
				err = CancelHandle(requestParam.Uint64Pks(), util.MapKeyValue(requestParam.EventConfirmFormData, "reason", ""))
			} else if requestParam.EventCodeEqual("immediate_handle") {
				// 立即执行
				err = ImmediateHandle(requestParam.Uint64Pks())
			} else if requestParam.EventCodeEqual("re_handle") {
				// 重新执行
				err = ReHandle(requestParam.Uint64Pks())
			}
			if err != nil {
				return
			}
			res.Reload = true
			return
		})
}

// 列表数据项处理
func (s *taskPage) listBuilderItemsHandle(items []*model.AdminTask, t *builder.Table) (resItems []*dto.TaskPageListItemResponse, err error) {
	if len(items) <= 0 {
		return
	}

	isRequestExportItems := t.IsRequestExportItems() // 是否请求导出items
	resItems = make([]*dto.TaskPageListItemResponse, 0, len(items))
	for _, item := range items {
		resItem := &dto.TaskPageListItemResponse{
			Type:        item.Type,
			Title:       item.Title,
			Status:      item.Status,
			CreatedAt:   item.CreatedAt.Format("2006-01-02 15:04:05"),
			RateDesc:    fmt.Sprintf("%d%%", item.Rate),
			LatencyDesc: util.LatencyDescByMs(item.Latency),
		}
		// task_id
		taskId, _ := util.ToString(item.Id)
		resItem.Id = taskId
		resItem.TaskId = taskId
		// member_name
		if item.Member != nil {
			resItem.MemberName = fmt.Sprintf("%s【%s】", item.Member.Name, item.Member.Username)
		}
		// 时间线描述
		timeLine := item.GetSnapTimeLine()
		if len(timeLine) > 0 {
			timeLineDesc := ""
			for i := len(timeLine) - 1; i >= 0; i-- {
				node := timeLine[i]
				if isRequestExportItems {
					nodeDesc := fmt.Sprintf(`%s %s 耗时：%s\r\n`, node.StatusDesc, node.Time, util.LatencyDescByMs(node.Latency))
					if len(node.Other) > 0 {
						if reason, exists := node.Other["reason"]; exists && reason != "" {
							nodeDesc += fmt.Sprintf("原因：%s\r\n", reason)
						}
					}
					timeLineDesc += nodeDesc
				} else {
					statusColorStyle := ""
					if nodeStatus, ok := taskenum.StatusEnum.IsValue(node.Status); ok {
						if nodeStatusColor := taskenum.StatusByValue(nodeStatus).GetColor(); nodeStatusColor != "" {
							statusColorStyle = "color:" + nodeStatusColor
						}
					}
					nodeDesc := fmt.Sprintf(`<div style="display:inline-block;font-weight:600;width:50px;text-align:right;%s">%s</div>
											<div style="display:inline-block;font-weight:600;padding-left:10px">%s</div>
											<div style="display:inline-block;font-weight:600;padding-left:10px">耗时：%s</div><br>`,
						statusColorStyle, node.StatusDesc, node.Time, util.LatencyDescByMs(node.Latency))
					if len(node.Other) > 0 {
						if reason, exists := node.Other["reason"]; exists && reason != "" {
							nodeDesc += fmt.Sprintf(`<div style="display:inline-block;margin-left:60px;"><span style="font-weight:600;color:#31BDEC;">原因：</span>%s</div><br>`, reason)
						}
					}
					timeLineDesc += fmt.Sprintf(`<div class="item">%s</div>`, nodeDesc)
				}
			}
			resItem.TimeLineDesc = timeLineDesc
		}
		// 导出文件下载链接
		resItem.ExportFileDownloadUrl = item.GetExportFileDownloadUrl()
		resItems = append(resItems, resItem)
	}
	return
}

// 列表过滤条件查询对象
func (s *taskPage) listBuilderFilterQuery(ctx *gin.Context, query *gorm.DB, requestParam *builderdto.TableRequestParam, filterFormParam *dto.TaskPageListFilterFormParam) (err error) {
	// pks
	if len(requestParam.Pks) > 0 {
		ids := make([]uint64, 0, len(requestParam.Pks))
		for _, v := range requestParam.Pks {
			id, _ := util.ToUint64(v)
			ids = append(ids, id)
		}
		query.Where("id in ?", ids)
	}
	// status
	if requestParam.State != consts.ALL_SELECT_VALUE {
		query.Where("status = ?", requestParam.State)
	}
	// member_id
	if adminservice.CheckAuth(ctx, "yunj_task_list_filter_member") {
		// 有管理员选择权限
		if filterFormParam.MemberId > 0 {
			query.Where("member_id = ?", filterFormParam.MemberId)
		}
	} else {
		// 没有管理员选择权限
		member := adminservice.GetLoginMember(ctx)
		if member == nil || member.Id <= 0 {
			err = errors.New("请登录后，执行此操作！")
			return
		}
		query.Where("member_id = ?", member.Id)
	}
	if filterFormParam.MemberId > 0 {
		query.Where("member_id = ?", filterFormParam.MemberId)
	}
	// type
	if typeValue, ok := taskenum.TypeEnum.IsValue(filterFormParam.Type); ok {
		query.Where("`type` = ?", typeValue)
	}
	// task_ids
	if filterFormParam.TaskIds != "" {
		taskIds := strings.Split(filterFormParam.TaskIds, ",")
		taskUint64Ids := make([]uint64, 0, len(taskIds))
		for _, taskId := range taskIds {
			taskUint64Id, err := util.ToUint64(taskId)
			if err != nil {
				continue
			}
			taskUint64Ids = append(taskUint64Ids, taskUint64Id)
		}
		query.Where("id in ?", taskUint64Ids)
	}
	// title
	if filterFormParam.Title != "" {
		query.Where("title like ?", "%"+filterFormParam.Title+"%")
	}
	return
}
